﻿//-----------------------------------------------------------------------
// <copyright file="CustomPagination.ascx.cs" company="CMSN Software">
//    Copyright © 2011  CMSN Software
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses.
// </copyright>
//-----------------------------------------------------------------------

namespace CMSN.Software
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Globalization;
    using System.Web.UI.WebControls;

    /// <summary>
    /// Enumeration for pagination mode
    /// </summary>
    public enum PaginationType
    {
        /// <summary>
        /// range mode
        /// </summary>
        Range,

        /// <summary>
        /// numeric mode
        /// </summary>
        Numeric
    }

    /// <summary>
    /// pagination control for repeater
    /// </summary>
    public partial class CustomPagination : System.Web.UI.UserControl
    {
        #region Public Properties
        /// <summary>
        /// Gets or sets the pagination mode.
        /// pagination points text display mode( page number, page range).
        /// </summary>
        /// <value>
        /// The pagination mode.
        /// </value>
        [DefaultValue(PaginationType.Range)]
        public PaginationType PaginationMode { get; set; }

        /// <summary>
        /// Gets or sets the pagination points count(number of paginations point to display)
        /// </summary>
        /// <value>
        /// The pagination points count.
        /// </value>
        public int PaginationPointsCount { get; set; }

        /// <summary>
        /// Gets or sets the size of the pagination (number of records per page).
        /// </summary>
        /// <value>
        /// The size of the pagination.
        /// </value>
        public int PaginationSize { get; set; }

        /// <summary>
        /// Gets or sets the repeater control to be paged.
        /// </summary>
        /// <value>
        /// The repeater control.
        /// </value>
        public Repeater RepeaterControl { get; set; }

        /// <summary>
        /// Gets or sets the data source.
        /// </summary>
        /// <value>
        /// The data source.
        /// </value>
        public IEnumerable DataSource { get; set; }

        /// <summary>
        /// Gets the current page.
        /// </summary>
        public int CurrentPage
        {
            get
            {
                return ViewState["_CurrentPage"] == null ? 0 : int.Parse(ViewState["_CurrentPage"].ToString(), CultureInfo.InvariantCulture);
            }

            private set
            {
                this.ViewState["_CurrentPage"] = value;
            }
        }

        /// <summary>
        /// Gets the page count.
        /// </summary>
        public int PageCount
        {
            get
            {
                return ViewState["_PageCount"] == null ? 0 : int.Parse(ViewState["_PageCount"].ToString(), CultureInfo.InvariantCulture);
            }

            private set
            {
                ViewState["_PageCount"] = value;
            }
        }

        /// <summary>
        /// Gets the Results count.
        /// </summary>
        public int Count
        {
            get
            {
                return ViewState["_Count"] == null ? 0 : int.Parse(ViewState["_Count"].ToString(), CultureInfo.InvariantCulture);
            }

            private set
            {
                ViewState["_Count"] = value;
            }
        }

        /// <summary>
        /// Gets the current page text.
        /// </summary>
        public string CurrentPageText
        {
            get
            {
                return ViewState["_CurrentPageText"] == null ? "0" : ViewState["_CurrentPageText"].ToString();
            }

            private set
            {
                this.ViewState["_CurrentPageText"] = value;
            }
        }

        /// <summary>
        /// Gets or sets the pagination point CSS class.
        /// </summary>
        /// <value>
        /// The pagination point CSS class.
        /// </value>
        public string PaginationPointCssClass { get; set; }

        /// <summary>
        /// Gets or sets the selected page CSS class.
        /// </summary>
        /// <value>
        /// The selected page CSS class.
        /// </value>
        public string SelectedPageCssClass { get; set; }

        /// <summary>
        /// Gets or sets the navigation CSS class.
        /// </summary>
        /// <value>
        /// The navigation CSS class.
        /// </value>
        public string NavigationCssClass { get; set; }

        /// <summary>
        /// Gets or sets the CSS class.
        /// </summary>
        /// <value>
        /// CSS class for the pagination control
        /// </value>
        public string CssClass { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether [show first last].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [show first last]; otherwise, <c>false</c>.
        /// </value>
        [DefaultValue(false)]
        public bool ShowFirstLast { get; set; }

        /// <summary>
        /// Gets or sets the first page link text.
        /// </summary>
        /// <value>
        /// The first page link text.
        /// </value>
        [DefaultValue("<<")]
        public string FirstPageLinkText { get; set; }

        /// <summary>
        /// Gets or sets the last page link text.
        /// </summary>
        /// <value>
        /// The last page link text.
        /// </value>
        [DefaultValue(">>")]
        public string LastPageLinkText { get; set; }

        /// <summary>
        /// Gets or sets the previous page link text.
        /// </summary>
        /// <value>
        /// The previous page link text.
        /// </value>
        [DefaultValue("<")]
        public string PreviousPageLinkText { get; set; }

        /// <summary>
        /// Gets or sets the next page link text.
        /// </summary>
        /// <value>
        /// The next page link text.
        /// </value>
        [DefaultValue(">")]
        public string NextPageLinkText { get; set; }

        #endregion

        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            // display the firs page and last page links based on the user selection.
            this.FirstPageLink.Visible = LastPageLink.Visible = this.ShowFirstLast;

            // generate the pagination controls at page load.
            this.GeneratePagination();

            // set the navigation links styles with user value.
            this.PreviousPageLink.CssClass = NextPageLink.CssClass = FirstPageLink.CssClass = LastPageLink.CssClass = this.NavigationCssClass;

            // set the first page link text
            if (!string.IsNullOrEmpty(this.FirstPageLinkText))
            {
                this.FirstPageLink.Text = this.FirstPageLinkText;
            }

            // set the last page link text
            if (!string.IsNullOrEmpty(this.LastPageLinkText))
            {
                this.LastPageLink.Text = this.LastPageLinkText;
            }

            // set previous page link text.
            if (!string.IsNullOrEmpty(this.PreviousPageLinkText))
            {
                this.PreviousPageLink.Text = this.PreviousPageLinkText;
            }

            // set the next page link text
            if (!string.IsNullOrEmpty(this.NextPageLinkText))
            {
                this.NextPageLink.Text = this.NextPageLinkText;
            }

            this.Pagination.CssClass = this.CssClass; // pagination bar style
        }

        /// <summary>
        /// Handles the Click event of the PaginationPoint control.
        /// adjust the pagination to fit selected page number.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void PaginationPoint_Click(object sender, EventArgs e)
        {
            LinkButton selectingPage = (LinkButton)sender;
            this.CurrentPage = int.Parse(selectingPage.CommandArgument, CultureInfo.InvariantCulture);
            this.GeneratePagination();
        }

        /// <summary>
        /// Handles the Click event of the PreviousPageLink control.
        /// go to the previous page
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void PreviousPageLink_Click(object sender, EventArgs e)
        {
            this.CurrentPage -= 1;
            this.GeneratePagination();
        }

        /// <summary>
        /// Handles the Click event of the NextPageLink control.
        /// go to next page.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void NextPageLink_Click(object sender, EventArgs e)
        {
            this.CurrentPage += 1;
            this.GeneratePagination();
        }

        /// <summary>
        /// Handles the Click event of the FirstPageLink control.
        /// move to first page
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void FirstPageLink_Click(object sender, EventArgs e)
        {
            this.CurrentPage = 0;
            this.GeneratePagination();
        }

        /// <summary>
        /// Handles the Click event of the LastPageLink control.
        /// move to last page
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void LastPageLink_Click(object sender, EventArgs e)
        {
            this.CurrentPage = this.PageCount - 1;
            this.GeneratePagination();
        }

        /// <summary>
        /// Creates the pagination point.
        /// </summary>
        /// <param name="title">title of the pagination point</param>
        /// <param name="index">index of the pagination point</param>
        /// <returns>Pagination point link</returns>
        private LinkButton CreatePaginationPoint(string title, int index)
        {
            LinkButton paginationPoint = new LinkButton();
            paginationPoint.ID = index.ToString(CultureInfo.InvariantCulture);
            paginationPoint.Text = title;
            paginationPoint.CssClass = this.PaginationPointCssClass;
            paginationPoint.CommandArgument = index.ToString(CultureInfo.InvariantCulture);
            paginationPoint.Click += new EventHandler(this.PaginationPoint_Click);
            return paginationPoint;
        }

        /// <summary>
        /// Generates the pagination.
        /// </summary>
        private void GeneratePagination()
        {
            PagedDataSource pagedDatasource = new PagedDataSource();
            pagedDatasource.DataSource = this.DataSource;   // set the data source
            pagedDatasource.AllowPaging = true;             // enable pagination
            pagedDatasource.PageSize = this.PaginationSize; // set the pagination size.
            pagedDatasource.CurrentPageIndex = this.CurrentPage;    // set the current page.
            this.PageCount = pagedDatasource.PageCount;     // set the number of pages
            this.Count = pagedDatasource.DataSourceCount;   // set the result count

            // set the max value of the pagination range when page size not define
            this.PaginationSize = (this.PaginationSize == 0) ? pagedDatasource.DataSourceCount : this.PaginationSize;

            // hide previous page link on first page.
            this.PreviousPageLink.Visible = !pagedDatasource.IsFirstPage;

            // hide next page link on last page.
            this.NextPageLink.Visible = !pagedDatasource.IsLastPage;

            // set the first and last link visibility based on user selection.
            if (this.ShowFirstLast)
            {
                // hide first page link on first page.
                this.FirstPageLink.Visible = !pagedDatasource.IsFirstPage;

                // hide last page link on last page.
                this.LastPageLink.Visible = !pagedDatasource.IsLastPage;
            }

            this.PaginationPoints.Controls.Clear(); // clear old pagination points.

            // pagination start index.
            int startIndex = this.CurrentPage - (this.PaginationPointsCount / 2);
            startIndex = startIndex < 0 ? 0 : startIndex; // minimum value must be 0.

            // last index of pagination points
            int endIndex = this.PaginationPointsCount + startIndex;

            // display value of pagination point.
            string paginationPointText;

            for (int i = startIndex; i < endIndex; i++)
            {
                if (this.PaginationMode == PaginationType.Numeric)
                {
                    // display page number as pagination point text.
                    paginationPointText = (i + 1).ToString(CultureInfo.CurrentCulture);
                }
                else
                {
                    // display page number as range.

                    // start number of the range.
                    int startNumber = ((i + 1) * this.PaginationSize) + 1 - this.PaginationSize;

                    // end value of range. if the range grater than maximum number, display max number.
                    int endNumber = (i + 1) * this.PaginationSize > pagedDatasource.DataSourceCount ?
                        pagedDatasource.DataSourceCount : (i + 1) * this.PaginationSize;
                    paginationPointText = startNumber + " - " + endNumber;
                }

                // selected page as a label and rest as links.
                if (i == this.CurrentPage)
                {
                    // set the selected page text.
                    this.CurrentPageText = paginationPointText;
                    using (Label selectedPage = new Label())
                    {
                        selectedPage.Text = paginationPointText;
                        selectedPage.CssClass = this.SelectedPageCssClass;
                        this.PaginationPoints.Controls.Add(selectedPage);
                    }
                }
                else if (i < pagedDatasource.PageCount)
                {
                    this.PaginationPoints.Controls.Add(this.CreatePaginationPoint(paginationPointText, i));
                }
            }

            // bind paged data to repeater
            this.RepeaterControl.DataSource = pagedDatasource;
            this.RepeaterControl.DataBind();
        }
    }
}